// imageTypes.cpp
//
// Definition of our supported image types

#include "image/imageTypes.h"
#include <limits.h>
#include <float.h>

namespace simple{
    // Note: Some compilers do not seem to have an implementation for
    //       std::numeric_limits<>. Because of this, we have to fall back
    //       to using limits.h to get our limit information
    //
    // Instead of writing,
    //   template<> LimitInfo<Pel8> LimitInfo<Pel8>::sType (std::numeric_limits<Pel8>::min(), 
    //                                                          std::numeric_limits<Pel8>::max());
    //
    // We use
    //   template<> LimitInfo<Pel8> LimitInfo<Pel8>::sType (0, UCHAR_MAX);


    //
    // LimitInfo
    //
    // Basic unsigned types
    template<> LimitInfo<Pel8> LimitInfo<Pel8>::sType (0, UCHAR_MAX);
    template<> LimitInfo<Pel16> LimitInfo<Pel16>::sType (0, USHRT_MAX);
    template<> LimitInfo<Pel32s> LimitInfo<Pel32s>::sType (INT_MIN, INT_MAX);
    template<> LimitInfo<Pel32> LimitInfo<Pel32>::sType (0, UINT_MAX);

    // Basic signed types
    template<> LimitInfo<char>  LimitInfo<char>::sType (SCHAR_MIN, SCHAR_MAX);
    template<> LimitInfo<short> LimitInfo<short>::sType (SHRT_MIN, SHRT_MAX);

    // Basic float types
    template<> LimitInfo<float>  LimitInfo<float>::sType (FLT_MIN, FLT_MAX);
    template<> LimitInfo<double> LimitInfo<double>::sType (DBL_MIN, DBL_MAX);

    // Pel types
    template<> LimitInfo<ClampedPel8> LimitInfo<ClampedPel8>::sType (0, UCHAR_MAX);
    template<> LimitInfo<ClampedPel16> LimitInfo<ClampedPel16>::sType (0, USHRT_MAX);
    template<> LimitInfo<ClampedPel32s> LimitInfo<ClampedPel32s>::sType (INT_MIN, INT_MAX);



    // RGB
    template<> LimitInfo<RGB> LimitInfo<RGB>::sType (RGB(0), RGB(UCHAR_MAX));
    template<> LimitInfo<RGBPel32> LimitInfo<RGBPel32>::sType (RGBPel32(0), RGBPel32(UINT_MAX));

    template<> LimitInfo<RGBPel32s> LimitInfo<RGBPel32s>::sType (RGBPel32s(INT_MIN), RGBPel32s(INT_MAX));

    template<> Pel8 Limit<Pel8, Pel32s> (const Pel32s& src)
    { return static_cast<Pel8> (src < 0 ? 0 : (src > 255 ? 255 : src));}

    template<> Pel8 Limit<Pel8, Pel8> (const Pel8& src)
    { return src;}

    template<> Pel32s Limit<Pel32s, Pel32s> (const Pel32s& src)
    { return src;}

    template<> Pel32s Limit<Pel32s, Pel32> (const Pel32& src)
    { return (src > INT_MAX) ? INT_MAX : src;}


    template<> ClampedPel8 Limit<ClampedPel8, ClampedPel32s> (const ClampedPel32s& src)
    { return static_cast<Pel8> (src.val < 0 ? 0 : (src.val > 255 ? 255 : src.val));}

    template<> ClampedPel8 Limit<ClampedPel8, ClampedPel8> (const ClampedPel8& src)
    { return src;}

    template<> ClampedPel32s Limit<ClampedPel32s, ClampedPel32s> (const ClampedPel32s& src)
    { return src;}


    // This specialization of Limit() will efficiently handle RGB values.
    // It also prevents us from having to worry about what operator<
    // means for an RGB value.
    template<> RGB Limit<RGB, RGBPel32s> (const RGBPel32s& src)
    {
        RGB dst;
        dst.red   = static_cast<Pel8> (src.red   < 0 ? 0 : (src.red   > 255 ? 255 : src.red));
        dst.green = static_cast<Pel8> (src.green < 0 ? 0 : (src.green > 255 ? 255 : src.green));
        dst.blue  = static_cast<Pel8> (src.blue  < 0 ? 0 : (src.blue  > 255 ? 255 : src.blue));
        return dst;
    }

    template<> RGB Limit<RGB, RGB> (const RGB& src)
    { return src;}

    template<> RGBPel32s Limit<RGBPel32s, RGBPel32s> (const RGBPel32s& src)
    { return src;}



    //
    // Specializations
    //
    template<>
    std::ostream& operator<< (std::ostream& stream, const ClampedTmpl<Pel8>& pel)
    {
      stream << (Pel32s)pel.val;
      return stream;
    }

    template<>
    std::ostream& operator<< (std::ostream& stream, const RGBTmpl<Pel8>& rgb)
    {
      stream << "(" << (Pel32s)rgb.red << "," << (Pel32s)rgb.green << "," << (Pel32s)rgb.blue << ")";
      return stream;
    }

}